/*
 * PGN_Reader : A simple pgn reader for chess games.
 * Copyright (C) 2011 Laurent BERNABE
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.gmail.bernabe.laurent.java_swing.pgn_reader.gui;

import ictk.boardgame.AmbiguousMoveException;
import ictk.boardgame.IllegalMoveException;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.concurrent.ArrayBlockingQueue;

import javax.swing.JPanel;

import com.gmail.bernabe.laurent.java_swing.pgn_reader.processing.ChessLibraryAdapter;
import com.gmail.bernabe.laurent.java_swing.pgn_reader.processing.ChessPiece;
import com.gmail.bernabe.laurent.java_swing.pgn_reader.processing.PlayingTape;
import com.gmail.bernabe.laurent.java_swing.pgn_reader.processing.libraries_adapters.ICTKLibraryAdapter;

public class BoardJPanel extends JPanel implements PlayingTape {
	
	public final static int CELL_DIMENSION = 50;
	public final static int PIECE_DIMENSION = 40;
	
	public BoardJPanel(PGNReaderFrame pgnFrame){
		setPreferredSize(new Dimension(400, 400));
		this.pgnFrame = pgnFrame;
	}

	@Override
	public void paint(Graphics graphics) {
		for(int cellRank=0; cellRank<8; cellRank++){
			for(int cellFile=0; cellFile<8; cellFile++){
				clearCell(graphics, cellFile, cellRank);
				paintAPieceByCellPosition(graphics, new BoardCell(cellFile, cellRank));
			}
		}
		if (currentPieceToAnimate != null){
			clearCell(graphics, currentPieceToAnimate.getStartCell().getFile(), currentPieceToAnimate.getStartCell().getRank());
			paintAPieceByAbsolutePosition(graphics, currentPieceToAnimate.getPieceValue(),
					currentPieceToAnimate.getCurrentX() , currentPieceToAnimate.getCurrentY());
		}
		if (! pendingAnimatedPieces.isEmpty() ){
			for (PieceToAnimate currentPendingPiece : pendingAnimatedPieces){
				clearCell(graphics, currentPendingPiece.getStartCell().getFile(), currentPendingPiece.getStartCell().getRank());
				paintAPieceByAbsolutePosition(graphics, currentPendingPiece.getPieceValue(),
						currentPendingPiece.getCurrentX() , currentPendingPiece.getCurrentY());
			}
		}
	}
	
	public void registerAnimation(BoardCell startCell, BoardCell endCell){
		boolean weStillCanInsertAnimation = animationsToLaunch.remainingCapacity()>0;
		if (weStillCanInsertAnimation) {
			final ChessPiece pieceToMove = chessLibrary.getPieceAt(startCell);
			PieceToAnimate pieceToAnimate = new PieceToAnimate(pieceToMove, this);
			pieceToAnimate.setStartPosition(startCell);
			pieceToAnimate.setEndPosition(endCell);
			try {
				animationsToLaunch.put(pieceToAnimate);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void launchAnimations(){
		pgnFrame.getButtonsPanel().setEnabled(false);
		if (! animationsToLaunch.isEmpty() ) {
			while (!animationsToLaunch.isEmpty()) {
				currentPieceToAnimate = animationsToLaunch
						.poll();
				currentPieceToAnimate.animate();
				try {
					currentPieceToAnimate.getAnimatorThread().join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				pendingAnimatedPieces.add(currentPieceToAnimate);
			}
			//TODO maybe place these lines in another method
			//for a smooth transition between animation end, and move validation
			currentPieceToAnimate = null;
			pendingAnimatedPieces.clear();
			///////////////////////////////////////////////////////////
		}
		pgnFrame.getButtonsPanel().setEnabled(true);
	}

	public ChessPiece getPieceAt(BoardCell wantedCell) {
		return chessLibrary.getPieceAt(wantedCell);
	}
	
	public static int getPieceAbsolutePosition(int cellColumnOrLine) {
		return cellColumnOrLine*CELL_DIMENSION+(CELL_DIMENSION - PIECE_DIMENSION)/2;
	}
	
	@Override
	public void gotoStart() {
		chessLibrary.gotoStart();
	}

	@Override
	public void gotoEnd() {
		chessLibrary.gotoEnd();
	}

	@Override
	public void playNextHalfMove() {
		chessLibrary.playNextHalfMove();
	}

	@Override
	public void goBackOneHalfMove() {
		chessLibrary.goBackOneHalfMove();
	}
	
	/**
	 * Loads data from a PGN file reference : all contained games should be stored
	 * in a collection. 
	 * @param pgnFile - File - the pgn file to load.
	 * @throws FileNotFoundException - if the file could not been found.
	 * @throws IllegalMoveException - if one move was illegal in a game.
	 * @throws AmbiguousMoveException - if one move was subject to several interpretations in a game.
	 */
	public void loadPGN(File pgnFile) throws FileNotFoundException,
	 IllegalMoveException, AmbiguousMoveException {
		chessLibrary.loadPGN(pgnFile);
	}
	
	/**
	 * Load a game in the game collection (filled by loadPGN) by his index.
	 * If index is incorrect, it will try to load first game.
	 * @param gameIndexInCollection - int - index of game in collection.
	 */
	public void loadGame(int gameIndexInCollection){
		chessLibrary.loadGame(gameIndexInCollection);
	}
	
	private void paintAPieceByAbsolutePosition(Graphics graphics, ChessPiece pieceToDraw,
			int positionX, int positionY){
		if (pieceToDraw != null) {
			Image piecePicture =  ImagesManager.getInstance().getPieceImage(pieceToDraw)
					.getScaledInstance(PIECE_DIMENSION, PIECE_DIMENSION, Image.SCALE_FAST);
			graphics.drawImage(piecePicture,
					positionX,
					positionY,
					this);
		}
	}
	
	private void paintAPieceByCellPosition(Graphics graphics, BoardCell cellPosition) {
		ChessPiece pieceToDraw = chessLibrary.getPieceAt(cellPosition);
		paintAPieceByAbsolutePosition(graphics, pieceToDraw, 
				getPieceAbsolutePosition(cellPosition.getFile()),
				getPieceAbsolutePosition(7-cellPosition.getRank()));
	} 
	
	private void clearCell(Graphics graphics, int cellFile, int cellRank) {
			Color cellColor;
			if ((cellFile+cellRank)%2 == 0)
				cellColor = WHITE_CELL_COLOR;
			else
				cellColor = BLACK_CELL_COLOR;
			paintACell(graphics, cellFile, cellRank, cellColor);
	}

	private void paintACell(Graphics graphics, int cellFile, int cellRank,
			Color cellColor) {
		int posX = cellFile * CELL_DIMENSION;
		int posY = cellRank * CELL_DIMENSION;
		graphics.setColor(cellColor);
		graphics.fillRect(posX, posY, CELL_DIMENSION, CELL_DIMENSION);
	}
	
	private static final long serialVersionUID = 1L;
	private final static Color WHITE_CELL_COLOR = new Color(255,255,160);
	private final static Color BLACK_CELL_COLOR = new Color(70,35,20);
	/**
	 * Animations to launch before the next move to register.
	 */
	private ArrayBlockingQueue<PieceToAnimate> animationsToLaunch = new ArrayBlockingQueue<PieceToAnimate>(2);
	private PieceToAnimate currentPieceToAnimate;
	/**
	 * Animated pieces that have finished their animations, and are waiting for the next move to be committed.
	 */
	private ArrayList<PieceToAnimate> pendingAnimatedPieces = new ArrayList<PieceToAnimate>(1);
	
	private PGNReaderFrame pgnFrame;
	private ChessLibraryAdapter chessLibrary = new ICTKLibraryAdapter();
}
